﻿using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;

/// <summary>
/// 日志文件类
/// </summary>
public static class Logger
{
	private static bool m_isInitial = false;
	private static string m_direc = null;
	private static FileStream m_fStream = null;
	private static StreamWriter m_wStream = null;
	private static string m_currentTime = null;
	private static object m_syncObj = new object();
	private static Thread m_writeThread = null;
	private static Queue m_msgQue = new Queue();
	private static LoggerType m_logType;
	private static long m_fileSize; // 50M

	/// <summary>
	/// 根据传入的日志类型初始化Logger
	/// </summary>
	/// <param name="type"></param>
	/// <param name="fileMaxSize">日志文件的最大大小，单位MB</param>
	public static void InitialLog_X(LoggerType type = LoggerType.Day, int fileMaxSize = 50)
	{
		lock (m_syncObj)
		{
			m_direc = Path.Combine(Environment.CurrentDirectory, "LogInfo");
			InitialLog_X(m_direc, type, fileMaxSize);
		}
	}

	/// <summary>
	///  根据传入的文件夹地址和日志类型初始化Logger
	/// </summary>
	/// <param name="directory"></param>
	public static void InitialLog_X(string directory, LoggerType type = LoggerType.Day, int fileMaxSize = 50)
	{
		lock (m_syncObj)
		{
			if (!string.IsNullOrEmpty(directory))
			{
				if (!Directory.Exists(directory))
				{
					Directory.CreateDirectory(directory);
					m_direc = directory;
				}

				if (m_msgQue == null)
				{
					m_msgQue = new Queue();
				}

				if (m_writeThread == null)
				{
					m_writeThread = new Thread(WriteMsg);
					m_writeThread.IsBackground = false;
					m_writeThread.Start();
				}

				m_fileSize = fileMaxSize * 1024 * 1024;
				m_logType = type;
				m_isInitial = true;
			}
			else
			{
				m_isInitial = false;
				throw new Exception("directory is null or empty!");
			}
		}
	}

	/// <summary>
	/// 写入一般信息
	/// </summary>
	/// <param name="info"></param>
	public static void Info(string info, bool bRecordStackTrace = false)
	{
		AddMsgQueue("Info", info, bRecordStackTrace);
	}

	/// <summary>
	/// 写入警告信息
	/// </summary>
	/// <param name="info"></param>
	public static void Warn(string info, bool bRecordStackTrace = false)
	{
		AddMsgQueue("Warn", info, bRecordStackTrace);
	}

	/// <summary>
	/// 写入错误信息
	/// </summary>
	/// <param name="info"></param>
	public static void Error(string info, bool bRecordStackTrace = false)
	{
		AddMsgQueue("Error", info, bRecordStackTrace);
	}

	/// <summary>
	/// 写入致命错误信息
	/// </summary>
	/// <param name="info"></param>
	public static void Fatal(string info, bool bRecordStackTrace = false)
	{
		AddMsgQueue("Fatal", info, bRecordStackTrace);
	}

	private static void DateTimeCheck()
	{
		string time = null;
		switch (m_logType)
		{
			case LoggerType.Once:
				{
					return;
				}

			case LoggerType.Minuter:
				{
					time = DateTime.Now.ToString("mm");
				}
				break;

			case LoggerType.Hour:
				{
					time = DateTime.Now.ToString("HH");
				}
				break;

			case LoggerType.Day:
				{
					time = DateTime.Now.ToString("dd");
				}
				break;

			case LoggerType.Month:
				{
					time = DateTime.Now.ToString("MM");
				}
				break;
		}

		lock (m_syncObj)
		{
			if (m_fStream != null)
			{
				// 文件大小超过限制时，创建新文件
				if (m_fStream.Length <= m_fileSize)
				{
					// 文件大小未超过限制，时间不相等时创建文件
					if (time == m_currentTime)
					{
						return;
					}
				}
			}
		}

		m_currentTime = time;

		CreateFileStream(GetFilePath());
	}

	private static void CreateFileStream(string file)
	{
		lock (m_syncObj)
		{
			if (m_fStream != null)
			{
				m_fStream.Flush();
				m_fStream.Close();
			}

			m_fStream = new FileStream(GetFilePath(), FileMode.Append, FileAccess.Write);
			m_wStream = new StreamWriter(m_fStream);
			m_wStream.AutoFlush = true;
		}
	}

	private static string GetFilePath()
	{
		return Path.Combine(m_direc, DateTime.Now.ToString("yyyyMMdd_HH-mm-ss")) + ".log";
	}

	private static void AddMsgQueue(string type, string msg, bool bRecordStackTrace)
	{
		lock (m_syncObj)
		{
			if (!m_isInitial)
			{
				InitialLog_X();
			}

			DateTimeCheck();

			if (bRecordStackTrace)
			{
				msg = string.Concat(msg, "\r\n[StackTrace]\t", RecordStackTrace());
			}

			string log = string.Format("[{0}]\t{1}\r\n{2}\r\n\r\n", type, DateTime.Now.ToString("HH:mm:ss"), msg);
			m_msgQue.Enqueue(log);
		}
	}

	private static void WriteMsg()
	{
		lock (m_syncObj)
		{
			if (!m_isInitial)
			{
				InitialLog_X();
			}
		}

		int msgCount = 0;
		while (true)
		{
			lock (m_syncObj)
			{
				msgCount = m_msgQue.Count;
				while (m_msgQue.Count > 0)
				{
					m_wStream.WriteLine(m_msgQue.Dequeue());
				}
			}

			// 根据当前写入数据量动态调整写入频率
			if (msgCount > 1000)
			{
				Thread.Sleep(10);
				continue;
			}
			else if (msgCount > 100)
			{
				Thread.Sleep(50);
				continue;
			}
			else
			{
				Thread.Sleep(100);
				continue;
			}
		}
	}

	/// <summary>
	/// FileStream文件流刷新
	/// </summary>
	public static void Flush()
	{
		m_fStream.Flush();
	}

	/// <summary>
	/// 关闭日志文件记录
	/// 调用后如果想再次使用Logger，必须重新初始化。
	/// </summary>
	public static void Close()
	{
		while (m_msgQue.Count > 0)
		{
			Thread.Sleep(10);
		}

		lock (m_syncObj)
		{
			try
			{
				m_wStream.Flush();
				m_fStream.Flush();
				m_wStream.Close();
				m_fStream.Close();
				m_writeThread.Abort();
			}
			catch
			{

			}
		}
	}

	/// <summary>
	/// 记录当前堆栈信息
	/// </summary>
	/// <returns></returns>
	public static string RecordStackTrace()
	{
		StackTrace st = new StackTrace(true);
		StackFrame[] sf = st.GetFrames();
		StringBuilder info = new StringBuilder(3000);
		for (int i = 0; i < sf.Length; ++i)
		{
			info.AppendFormat("\r\nFileName={0} methodFullname={1} function={2} FileLineNumber={3}", sf[i].GetFileName(), sf[i].GetMethod().DeclaringType.FullName, sf[i].GetMethod().Name, sf[i].GetFileLineNumber());
		}

		return string.Format("callback is :{0}", info);
	}
}

/// <summary>
/// Log记录类型
/// </summary>
public enum LoggerType
{
	Once,
	Minuter,
	Hour,
	Day,
	Month,
	/*Custom*/
}
